/*
   Last change:  RM   17 Jan 2001   20:15
*/

//*****************************************************************************
//
//      COPYRIGHT (c) ATMEL Norway, 1996-2001
//
//      The copyright to the document(s) herein is the property of
//      ATMEL Norway, Norway.
//
//      The document(s) may be used  and/or copied only with the written
//      permission from ATMEL Norway or in accordance with the terms and
//      conditions stipulated in the agreement/contract under which the
//      document(s) have been supplied.
//
//*****************************************************************************
//
//  File........: DATAFLASH.C
//
//  Author(s)...: ATMEL Norway
//
//  Target(s)...: All AVRs with built-in HW SPI
//
//  Description.: Functions to access the Atmel AT45Dxxx dataflash series
//              Supports 512Kbit - 64Mbit
//
//  Revisions...:
//
//  YYYYMMDD - VER. - COMMENT                                       - SIGN.
//
//  20011017 - 1.00 - Beta release                                  -  RM
//  20011017 - 0.10 - Generated file                                -  RM
//
//*****************************************************************************


// Includes
//#include <INA90.H>
//#include "iom169.h"

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "dataflash.h"
//#include "string.h"

// Constants
//Look-up table for these sizes ->  512k, 1M, 2M, 4M, 8M, 16M, 32M, 64M
unsigned char DF_pagebits[]  ={  9,  9,  9,  9,  9,  10,  10,  11};        //index of internal page address bits
//Look-up table for these sizes ->  512k, 1M,  2M,  4M,  8M, 16M, 32M, 64M
unsigned int  DF_pagesize[]  ={264,264, 264, 264, 264, 528, 528,1056};     //index of pagesizes


// Globals
unsigned char PageBits;
unsigned int  PageSize;
// Functions

/*****************************************************************************
*
*   Function name : DF_SPI_init
*
*   Returns :      None
*
*   Parameters :   None
*
*   Purpose :      Sets up the HW SPI in Master mode, Mode 3
*               Note -> Uses the SS line to control the DF CS-line.
*
******************************************************************************/
void DF_SPI_init (void)
{
   
   PORTB |= (1<<PORTB3) | (1<<PORTB2) | (1<<PORTB1) | (1<<PORTB0);
   DDRB |= (1<<PORTB2) | (1<<PORTB1) | (1<<PORTB0);                                 //Set MOSI, SCK AND SS as outputs
   SPSR = (1<<SPI2X);                                                               //SPI double speed settings
   SPCR = (1<<SPE) | (1<<MSTR) | (1<<CPHA) | (1<<CPOL);                             //Enable SPI in Master mode, mode 3, Fosc/2
//   SPCR = (1<<SPE) | (1<<MSTR) | (1<<CPHA) | (1<<CPOL) | (1<<SPR1) | (1<<SPR0);   //Enable SPI in Master mode, mode 3, Fosc/2
}

/*****************************************************************************
*
*   Function name : DF_SPI_RW
*
*   Returns :      Byte read from SPI data register (any value)
*
*   Parameters :   Byte to be written to SPI data register (any value)
*
*   Purpose :      Read and writes one byte from/to SPI master
*
******************************************************************************/
unsigned char DF_SPI_RW (unsigned char output)
{
   unsigned char input;
   
   SPDR = output;                                                                   //put byte 'output' in SPI data register
   while(!(SPSR & 0x80));                                                           //wait for transfer complete, poll SPIF-flag
   input = SPDR;                                                                    //read value in SPI data reg.
   
   return input;                                                                    //return the byte clocked in from SPI slave
}      


/*****************************************************************************
*
*   Function name : Read_DF_status
*
*   Returns :      One status byte. Consult Dataflash datasheet for further
*               decoding info
*
*   Parameters :   None
*
*   Purpose :      Status info concerning the Dataflash is busy or not.
*               Status info concerning compare between buffer and flash page
*               Status info concerning size of actual device
*
******************************************************************************/
unsigned char Read_DF_status (void)
{
   unsigned char result;
   
   DF_CS_inactive;                                                                  //make sure to toggle CS signal in order
   DF_CS_active;                                                                    //to reset dataflash command decoder
   result = DF_SPI_RW(StatusReg);                                                   //send status register read op-code
   result = DF_SPI_RW(0x00);                                                        //dummy write to get result
      
   //index_copy = ((result & 0x38) >> 3);                                             //get the size info from status register
   PageBits   = DF_pagebits[0];                                            //get number of internal page address bits from look-up table
   PageSize   = DF_pagesize[0];                                            //get the size of the page (in bytes)
   return result;                                                                   //return the read status register value
}


// *********************************************************************************************************************************************************
                        //WRITE String

// *********************************************************************************************************************************************************


/*****************************************************************************
*
*   Function name : Buffer_Write_Str
*
*   Returns :      None
*
*   Parameters :   BufferNo   ->   Decides usage of either buffer 1 or 2
*               IntPageAdr   ->   Internal page address
*               No_of_bytes   ->   Number of bytes to be written
*               *BufferPtr   ->   address of buffer to be used for copy of bytes
*                           from AVR buffer to dataflash buffer 1 (or 2)
*
*   Purpose :      Copies one or more bytes to one of the dataflash
*               internal SRAM buffers from AVR SRAM buffer
*               pointed to by *BufferPtr
*
******************************************************************************/
void Buffer_Write_Str (unsigned char BufferNo, unsigned int IntPageAdr, unsigned int No_of_bytes, unsigned char *BufferPtr)
{
   unsigned int i;

   DF_CS_inactive;                                                                  //make sure to toggle CS signal in order
   DF_CS_active;                                                                    //to reset dataflash command decoder
   
   if (1 == BufferNo)                                                               //write byte(s) to buffer 1
   {
      DF_SPI_RW(Buf1Write);                                                         //buffer 1 write op-code
      DF_SPI_RW(0x00);                                                              //don't cares
      DF_SPI_RW((unsigned char)(IntPageAdr>>8));                                    //upper part of internal buffer address
      DF_SPI_RW((unsigned char)(IntPageAdr));                                       //lower part of internal buffer address
      for( i=0; i<No_of_bytes; i++)
      {
         DF_SPI_RW(*(BufferPtr));                                                   //write byte pointed at by *BufferPtr to dataflash buffer 1 location
         BufferPtr++;                                                               //point to next element in AVR buffer
      }
   }

#ifdef USE_BUFFER2
   else
   if (2 == BufferNo)                                                               //write byte(s) to buffer 2
   {
      DF_SPI_RW(Buf2Write);                                                         //buffer 2 write op-code
      DF_SPI_RW(0x00);                                                              //don't cares
      DF_SPI_RW((unsigned char)(IntPageAdr>>8));                                    //upper part of internal buffer address
      DF_SPI_RW((unsigned char)(IntPageAdr));                                       //lower part of internal buffer address
      for( i=0; i<No_of_bytes; i++)
      {
         DF_SPI_RW(*(BufferPtr));                                                   //write byte pointed at by *BufferPtr to dataflash buffer 2 location
         BufferPtr++;                                                               //point to next element in AVR buffer
      }
   }
#endif
}
//NB : Monitorer busy-flag i status-reg.
//NB : Sjekk at (IntAdr + No_of_bytes) < buffersize, hvis ikke blir det bare ball..



/*****************************************************************************
*
*   Function name : Buffer_To_Page
*
*   Returns :      None
*
*   Parameters :   BufferAdr   ->   Decides usage of either buffer 1 or 2
*               PageAdr      ->   Address of flash page to be programmed
*
*   Purpose :      Transfers a page from dataflash SRAM buffer to flash
*               
******************************************************************************/
void Buffer_To_Page (unsigned char BufferNo, unsigned int PageAdr)
{
   DF_CS_inactive;                                                                  //make sure to toggle CS signal in order
   DF_CS_active;                                                                    //to reset dataflash command decoder
      
   if (1 == BufferNo)                                                               //program flash page from buffer 1
   {
      DF_SPI_RW(Buf1ToFlashWE);                                                     //buffer 1 to flash with erase op-code
      DF_SPI_RW((unsigned char)(PageAdr >> (16 - PageBits)));                       //upper part of page address
      DF_SPI_RW((unsigned char)(PageAdr << (PageBits - 8)));                        //lower part of page address
      DF_SPI_RW(0x00);                                                              //don't cares
   }

#ifdef USE_BUFFER2
   else   
   if (2 == BufferNo)                                                               //program flash page from buffer 2
   {
      DF_SPI_RW(Buf2ToFlashWE);                                                     //buffer 2 to flash with erase op-code
      DF_SPI_RW((unsigned char)(PageAdr >> (16 - PageBits)));                       //upper part of page address
      DF_SPI_RW((unsigned char)(PageAdr << (PageBits - 8)));                        //lower part of page address
      DF_SPI_RW(0x00);                                                              //don't cares
   }
#endif
   
   DF_CS_inactive;                                                                  //initiate flash page programming
   DF_CS_active;                                    
   
   while(!(Read_DF_status() & 0x80));                                               //monitor the status register, wait until busy-flag is high
}


/*****************************************************************************
*
*   Function name : Write_Flash_Memory
*
*   Returns :      None
*
*   Parameters :   BufferNo   ->   Decides usage of either buffer 1 or 2
*               IntPageAdr   ->   Internal page address ---> Uses always the Address 0x00
*      PageAdr      ->   Address of flash page to be programmed
*               No_of_bytes   ->   Number of bytes to be written ---> Uses strlen from string.h
*               *BufferPtr   ->   address of buffer to be used for copy of bytes
*                           from AVR buffer to dataflash buffer 1 (or 2)
*
*   Purpose :      Copies one or more bytes to one of the dataflash
*               internal SRAM buffers from AVR SRAM buffer
*               pointed to by *BufferPtr
*
******************************************************************************/

void Write_Flash_Memory (unsigned int No_bytes, unsigned char *BufferPtr)
{
   //strlen(const char *s) 
//Buffer_Write_Str (unsigned char BufferNo, unsigned int IntPageAdr, unsigned int No_of_bytes, unsigned char *BufferPtr)
//Buffer_To_Page (unsigned char BufferNo, unsigned int PageAdr)
   //unsigned int l;
  // l=strlen(BufferPtr);
   DF_SPI_init();
   Buffer_Write_Str(1,0,No_bytes,BufferPtr);
   Buffer_To_Page (1,0);
}


// *********************************************************************************************************************************************************
//                        READ Page

// *********************************************************************************************************************************************************

/*****************************************************************************
*
*   Function name : Page_To_Buffer
*
*   Returns :      None
*
*   Parameters :   BufferNo   ->   Decides usage of either buffer 1 or 2
*               PageAdr      ->   Address of page to be transferred to buffer
*
*   Purpose :      Transfers a page from flash to dataflash SRAM buffer
*               
******************************************************************************/
void Page_To_Buffer (unsigned int PageAdr, unsigned char BufferNo)
{
   DF_CS_inactive;                                                                  //make sure to toggle CS signal in order
   DF_CS_active;                                                                    //to reset dataflash command decoder
   
   if (1 == BufferNo)                                                               //transfer flash page to buffer 1
   {
      DF_SPI_RW(FlashToBuf1Transfer);                                               //transfer to buffer 1 op-code
      DF_SPI_RW((unsigned char)(PageAdr >> (16 - PageBits)));                       //upper part of page address
      DF_SPI_RW((unsigned char)(PageAdr << (PageBits - 8)));                        //lower part of page address
      DF_SPI_RW(0x00);                                                              //don't cares
   }
#ifdef USE_BUFFER2
   else   
   if (2 == BufferNo)                                                               //transfer flash page to buffer 2
   {
      DF_SPI_RW(FlashToBuf2Transfer);                                               //transfer to buffer 2 op-code
      DF_SPI_RW((unsigned char)(PageAdr >> (16 - PageBits)));                       //upper part of page address
      DF_SPI_RW((unsigned char)(PageAdr << (PageBits - 8)));                        //lower part of page address
      DF_SPI_RW(0x00);                                                              //don't cares
   }
#endif
   
   DF_CS_inactive;                                                                  //initiate the transfer
   DF_CS_active;
   
   while(!(Read_DF_status() & 0x80));                                               //monitor the status register, wait until busy-flag is high
}


/*****************************************************************************
*
*   Function name : Buffer_Read_Str
*
*   Returns :      None
*
*   Parameters :   BufferNo   ->   Decides usage of either buffer 1 or 2
*               IntPageAdr   ->   Internal page address
*               No_of_bytes   ->   Number of bytes to be read
*               *BufferPtr   ->   address of buffer to be used for read bytes
*
*   Purpose :      Reads one or more bytes from one of the dataflash
*               internal SRAM buffers, and puts read bytes into
*               buffer pointed to by *BufferPtr
*
******************************************************************************/
void Buffer_Read_Str (unsigned char BufferNo, unsigned int IntPageAdr, unsigned int No_of_bytes, unsigned char *BufferPtr)
{
   unsigned int i;

   DF_CS_inactive;                                                                  //make sure to toggle CS signal in order
   DF_CS_active;                                                                    //to reset dataflash command decoder
   
   if (1 == BufferNo)                                                               //read byte(s) from buffer 1
   {
      DF_SPI_RW(Buf1Read);                                                          //buffer 1 read op-code
      DF_SPI_RW(0x00);                                                              //don't cares
      DF_SPI_RW((unsigned char)(IntPageAdr>>8));                                    //upper part of internal buffer address
      DF_SPI_RW((unsigned char)(IntPageAdr));                                       //lower part of internal buffer address
      DF_SPI_RW(0x00);                                                              //don't cares
      for( i=0; i<No_of_bytes; i++)
      {
         *(BufferPtr) = DF_SPI_RW(0x00);                                            //read byte and put it in AVR buffer pointed to by *BufferPtr
         BufferPtr++;                                                               //point to next element in AVR buffer
      }
   }
   
#ifdef USE_BUFFER2
   else
   if (2 == BufferNo)                                                               //read byte(s) from buffer 2
   {
      DF_SPI_RW(Buf2Read);                                                          //buffer 2 read op-code
      DF_SPI_RW(0x00);                                                              //don't cares
      DF_SPI_RW((unsigned char)(IntPageAdr>>8));                                    //upper part of internal buffer address
      DF_SPI_RW((unsigned char)(IntPageAdr));                                       //lower part of internal buffer address
      DF_SPI_RW(0x00);                                                              //don't cares
      for( i=0; i<No_of_bytes; i++)
      {
         *(BufferPtr) = DF_SPI_RW(0x00);                                            //read byte and put it in AVR buffer pointed to by *BufferPtr
         BufferPtr++;                                                               //point to next element in AVR buffer
      }
   }
#endif
}
//NB : Sjekk at (IntAdr + No_of_bytes) < buffersize, hvis ikke blir det bare ball..


/*****************************************************************************
*
*   Function name : Buffer_Read_Str
*
*   Returns :      None
*
*   Parameters :   BufferNo   ->   Decides usage of either buffer 1 or 2
*               IntPageAdr   ->   Internal page address  ---> Uses always the Address 0x00
*               No_of_bytes   ->   Number of bytes to be read ---> 254 All Buffer
*               *BufferPtr   ->   address of buffer to be used for read bytes
*
*   Purpose :      Reads one or more bytes from one of the dataflash
*               internal SRAM buffers, and puts read bytes into
*               buffer pointed to by *BufferPtr
*
******************************************************************************/

void Read_Flash_Memory (unsigned int No_of_bytes, unsigned char *BufferPtr)
{
//Page_To_Buffer (unsigned int PageAdr, unsigned char BufferNo)
//Buffer_Read_Str (unsigned char BufferNo, unsigned int IntPageAdr, unsigned int No_of_bytes, unsigned char *BufferPtr)
   DF_SPI_init();
   Page_To_Buffer (0,1);
   Buffer_Read_Str (1, 0 , No_of_bytes, BufferPtr);

}

// *********************************************************************************************************************************************************
                        //Erase Page

// *********************************************************************************************************************************************************



/*****************************************************************************
*
*   Function name : Erase_Page
*
*   Returns :      None
*
*   Parameters     PageAdr      ->   Address of flash page to be programmed
*
*   Purpose :      
*               
******************************************************************************/
void Erase_Page ()
{
DF_SPI_init();
unsigned int PageAdr=0;
   DF_CS_inactive;                                                                  //make sure to toggle CS signal in order
   DF_CS_active;                                                                    //to reset dataflash command decoder
      
      DF_SPI_RW(PageErase);                                                         //erase-page op-code
      DF_SPI_RW((unsigned char)(PageAdr >> (16 - PageBits)));                       //upper part of page address
      DF_SPI_RW((unsigned char)(PageAdr << (PageBits - 8)));                        //lower part of page address
      DF_SPI_RW(0x00);                                                              //don't cares
   
   DF_CS_inactive;                                                                  //initiate flash page programming
   DF_CS_active;                                    
   
   while(!(Read_DF_status() & 0x80));                                               //monitor the status register, wait until busy-flag is high
}




// *****************************[ End Of DATAFLASH.C ]*************************
